hdu1671(trie树(字典树))

154 篇文章 0 订阅
82 篇文章 0 订阅

Description

Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let’s say the phone catalogue listed these numbers: 
1. Emergency 911 
2. Alice 97 625 999 
3. Bob 91 12 54 26 
In this case, it’s not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob’s phone number. So this list would not be consistent. 
 

Input

The first line of input gives a single integer, 1 <= t <= 40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1 <= n <= 10000. Then follows n lines with one unique phone number on each line. A phone number is a sequence of at most ten digits.
 

Output

For each test case, output “YES” if the list is consistent, or “NO” otherwise.
 

Sample Input

    
    
2 3 911 97625999 91125426 5 113 12340 123440 12345 98346
 

Sample Output

    
    
NO YES


首先贴上trie树的模板,然后这一题可以根据模板改一下子就ok:

#include <iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int NODE = 1e5+10,CH = 26;
/****
INPUT:
(Lower case)
输入n,m
输入n个串s1[i]
输入m个串s2[i]
OUTPUT:
对于每个s2[i] 在s1[i]中查找是否存在
存在则输出所在s1[i]中的标号  否则输出0
*/


struct Trie
{
    int ch[NODE][CH],sz,val[NODE];
    /**
    ch[u][c]:   节点u指向的c儿子的边
    val[u]:     节点u的值
    sz:         trie树的size
    */
    inline int idx(char c)
    {
        return c-'a';
    }
    int node()
    {
        /**
        新建(初始化)节点
        **/
        memset(ch[sz],0,sizeof(ch[sz]));
        ///将所有儿子置为空
        val[sz]=0;
        return sz++;
    }
    void init()
    {
        sz=0;
        ///trie树根节点为下标0的节点
        node();
    }
    void insert(char *s,int v)
    {
        int u=0;
        for(;*s;s++)
        {
            int c=idx(*s);
            ///如果节点不存在 新建节点 并把值赋给当前节点的c儿子边
            if(!ch[u][c])
                ch[u][c]=node();
            ///继续移动
            u=ch[u][c];
        }
        ///在末尾节点记录信息
        val[u]=v;
    }
    int find(char *s)
    {
        int u=0;
        for(;*s;s++)
        {
            int c=idx(*s);
            ///如果u节点没有c儿子  结束
            if(!ch[u][c])
                return 0;
            u=ch[u][c];
        }
        return val[u];
    }
}soul;

char s[10000];

int main()
{
    int n,m;
    soul.init();
    scanf("%d%d\n",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",s),soul.insert(s,i);
    while(m--)
    {
        scanf("%s",s);
        printf("%d\n",soul.find(s));
    }
    return 0;
}


这题有一点点不一样的就是在原模板基础上增加了一个num数组,用这个数组来干嘛呢?当然是记录数据呀~~~~~~神马数据需要记录??这就需要深入理解trie树的深刻含义了:

假设以串:
he
she
his
hers
构造Trie树 可以得到下图


这就是trie树的构建,构建完成之后会发现其实对应的每个公共节点出来的边的ch[i][j]的i都一样!一样的话这就说明求前缀是有路可寻的!因为我们虽然一个个判断前缀时间可能很慢,但是!现在发现了这个规律,那么我们把每个这种节点使用的次数记录下来,这样就可以表示经过这个节点的有多少线段,相当于一种dp思想,经过一次这个节点的数量就增加一次,最后公共前缀就显而易见的在num数组里了。而这题我们将所有数据先存,后来在一个个使用这些数据,那么如果没有前缀肯定是会走到自己的节点末端的,所以num数组求出来的值至少是1,当然,大于1便说明有更长的单词经过了这个节点,那么自然有前缀啦~~~~~

下面是这题解题代码:

#include <iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int NODE = 1e6+10,CH = 26;

int ch[NODE][CH],sz,val[NODE],num[NODE];
char a1[100001][15],a2[15];
    /**
    ch[u][c]:   节点u指向的c儿子的边
    val[u]:     节点u的值
    sz:         trie树的size
    */
int idx(char c)
{return c-'0';}
int node()
{
        /**
        新建(初始化)节点
        **/
        memset(ch[sz],0,sizeof(ch[sz]));
        ///将所有儿子置为空
        val[sz]=0;
        return sz++;
}
void init()
{
        sz=0;
        ///trie树根节点为下标0的节点
        node();
}
void insert(char *s,int v)
{
        int u=0;
        num[u]++;
        for(;*s;s++)
        {

            int c=idx(*s);
            ///如果节点不存在 新建节点 并把值赋给当前节点的c儿子边
            if(!ch[u][c])
                ch[u][c]=node();
            ///继续移动
          //  cout<<u<<' '<<char(c+'a')<<' '<<ch[u][c]<<endl;
            u=ch[u][c];num[u]++;
        }
        ///在末尾节点记录信息
        val[u]=v;
}
int find(char *s)
{
    int u=0;
    for(;*s;s++)
    {
        int c=idx(*s);
       // cout<<u<<' '<<(char)(c+'a')<<' '<<num[u]<<endl;
        ///如果u节点没有c儿子  结束
            if(!ch[u][c])
            return 0;
            u=ch[u][c];
       // cout<<(char)*s<<endl;
    }
    return num[u];
}
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
     init();
    memset(num,0,sizeof(num));
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
       scanf("%s",a1[i]);
        insert(a1[i],i+1);
    }
    int flag=1;
    for(int i=0;i<n;i++)
    {
        if(find(a1[i])>1)
            {printf("NO\n");flag=0;break;}
    }
    if(flag==1)printf("YES\n");
    }
    return 0;
}











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值